/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Implementation of wxWidgets base class 'MainFrame'
 * \author Antti Maula <antti.maula@tkk.fi>
 * \author wxFormBuilder (Autogenerated base code)
 *
 * $Id: MaCI_AlarmMainFrame.cpp,v 1.15 2009-04-27 13:00:54 amaula Exp $
 *
 */

#include "MaCI_AlarmMainFrame.h"
#include "MaCI_AlarmEventsConfigDialog.h"
#include "MaCI_AlarmQuestionDialog.h"
#include "MaCI_AlarmAboutDialog.h"
#include "MaCI_AlarmVersion.hpp"
#include "AlarmClient.hpp"
#include "owndebug.h"
#include "ownutils.h"




DEFINE_EVENT_TYPE(MY_EVENT_REFRESHEVENTLIST);
DEFINE_EVENT_TYPE(MY_EVENT_UPDATESTATUSBAR);
DEFINE_EVENT_TYPE(MY_EVENT_UNANSWEREDQUESTIONTIMEOUTUPDATE);
DEFINE_EVENT_TYPE(MY_EVENT_QUESTIONDIALOGEVENT);


//cretae event table for catching custom event
BEGIN_EVENT_TABLE(MaCI_AlarmMainFrame, wxFrame)
  EVT_COMMAND(wxID_ANY, MY_EVENT_REFRESHEVENTLIST, MaCI_AlarmMainFrame::OnAlarmMessage)
  EVT_COMMAND(wxID_ANY, MY_EVENT_UPDATESTATUSBAR, MaCI_AlarmMainFrame::OnUpdateStatusBar)
  EVT_COMMAND(wxID_ANY, MY_EVENT_QUESTIONDIALOGEVENT, MaCI_AlarmMainFrame::OnQuestionDialogEvent)
  EVT_TIMER(MY_EVENT_UNANSWEREDQUESTIONTIMEOUTUPDATE, MaCI_AlarmMainFrame::OnUnansweredQuestionTimeoutUpdateTimer)
END_EVENT_TABLE();
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
static std::string gConfigFileName = "MaCI_Alarm.state";
static std::string gEventsDisplayConfigRootKey = "EventsDisplayConfig";
//*****************************************************************************
//*****************************************************************************


//*****************************************************************************
//*****************************************************************************

MaCI_AlarmMainFrame::MaCI_AlarmMainFrame( wxWindow* parent )
  : MainFrame( parent ),
    iServiceList(),
    iServiceListUpdated(false),
    iAlarmClientArray(),
    iAlarmInfoArray(),
    iGIMIPtr(NULL),
    iEventsDisplayConfig(),
    iUnansweredQuestionTimeoutUpdateTimer(),
    iUnansweredQuestionActiveEntry(NULL)
{
  this->SetTitle( wxT("MaCI Alarm - ") + MaCI_AlarmVersion::GetVersionString() );

  // Set some visibility options
  unansweredQuestionPanel->Show(false);
  answeredQuestionPanel->Show(false);

  // Initialize Event list, creating four columns.
  eventListCtrl->InsertColumn(0, wxT("Time"));
  eventListCtrl->SetColumnWidth(0, 80);

  eventListCtrl->InsertColumn(1, wxT("Type"));
  eventListCtrl->SetColumnWidth(1, 90);

  eventListCtrl->InsertColumn(2, wxT("Source"));
  eventListCtrl->SetColumnWidth(2, 110);

  eventListCtrl->InsertColumn(3, wxT("Heading"));
  eventListCtrl->SetColumnWidth(3, 500);

  // Setup timer.
  iUnansweredQuestionTimeoutUpdateTimer.SetOwner(this, MY_EVENT_UNANSWEREDQUESTIONTIMEOUTUPDATE);

}
//*****************************************************************************

MaCI_AlarmMainFrame::~MaCI_AlarmMainFrame()
{
  Deinitialize();
}
//*****************************************************************************

bool MaCI_AlarmMainFrame::Initialize(gimi::GIMI *aGimiPtr, int aGimboID, int aReportID)
{
  bool result = true;
  
  // Store for future reference.
  iGIMIPtr = aGimiPtr;
  
  // Start the Gimbo core.
  CComponent::Start(aGimiPtr, aGimboID, aReportID);

  // Unconditional LoadConfig.
  LoadConfig();

  return result;
}
//*****************************************************************************

void MaCI_AlarmMainFrame::Deinitialize()
{
  DisconnectServices();
  //CComponent::Stop(); \todo Unimplemented.
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnServiceAssignment(const Gimbo::TServiceAssignmentList &aServiceList)
{
  dPrint(ODSTATUS,"Event: OnServiceAssignment");
  
  // Got service list - REPLACING current.
  iServiceList = aServiceList;
  iServiceListUpdated = true;
  
  // Disconnect services (if any)
  DisconnectServices();

  dPrint(ODTEST,"Disconnect - Done");

  // Connect services.
  ConnectServices();

  RequestUpdateStatusBar();

  dPrint(ODTEST,"Connect - Done");
  
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnGimboDetach()
{
  // Detached from Gimbo - Oh dear - or... So?
  dPrint(1,"Event: OnGimboDetach");
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnAlarmDataEvent(const MaCI::Alarm::CAlarmData &aData, 
                                           const unsigned int aDataSequence,
                                           MaCI::Alarm::CAlarmClient &aAlarmClient)
{
  CSync::Lock();
  SAlarmInfo ai(aData, &aAlarmClient);
  if (aData.GetTimestamp()) {
    ai.SetEntryTimestamp(aData.GetTimestamp());
  }
  iAlarmInfoArray.push_back(ai);

#warning Purging old entries from the original list seems to be 
#warning broken (Causes Segmentation faults). This needs to be fixed at 
#warning some point, otherwise the App will eat up memory as long as it is 
#warning active and messages are received.

  /** \todo Clearing the Received Events array causes problems. This
   * implementation should be though over and reimplemented.
   ****************

  if (iAlarmInfoArray.size() > iEventsDisplayConfig.eventLimit << 1) {

    // Iterate BACKWARDS
    typeof(iAlarmInfoArray.begin()) i = iAlarmInfoArray.begin();
    
    while(iAlarmInfoArray.size() > iEventsDisplayConfig.eventLimit<<1 &&
          i != iAlarmInfoArray.end()) {
      typeof(iAlarmInfoArray.begin()) tbd = i;
      ++i;
      
      // Is protected entry?
      if (tbd->IsProtected() == false) {
        // NOT!
        iAlarmInfoArray.erase(tbd);
      }
    }

    }*/
  RequestRefreshEventList();
  CSync::Unlock();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::UpdateQuestionEntry(SAlarmInfo *ai)
{ 
  const MaCI::Alarm::TText *t = ai->alarm.GetText();
  const MaCI::Alarm::TAlarmEvent *ae = ai->alarm.GetAlarmEvent();
  const MaCI::Common::TTimestamp *ts = ai->alarm.GetTimestamp(); 

  // Now inspect the event type.
  if (ae) {
    // Store the current display states of the question boxes (used
    // later for determining whether Layout() needs to be called)
    const bool box1 = answeredQuestionPanel->IsShown();
    const bool box2 = unansweredQuestionPanel->IsShown();

    // Is this a Question type?
    if (ae->type == MaCI::Alarm::KEventTypeQuestion) {
      // Was question. Now inspect the ANSWER status of this question.
      EQuestionAnswer a = ai->GetAnswer();
      if (a == KAnswerNone) {

        // Show panel
        unansweredQuestionPanel->Show(true);
        answeredQuestionPanel->Show(false);

        // Fill in pointer to Active Entry
        iUnansweredQuestionActiveEntry = ai;

        // Calculate timeleft.
        const unsigned int entry_timeout = ae->timeout_s;
        const gim::time absoluteTimeout = ai->GetEntryTimestamp() + (entry_timeout * 1000);
        const gim::time timeleft = absoluteTimeout - gim::time(true);
        const int timeleft_s = timeleft.getSeconds();

        // Check for timing information, handle timers and stuff here.
        if (timeleft_s > 0) {
          unansweredQuestiontimeoutLeftPanel->Show(true);
          unansweredQuestionTimeoutStaticText->SetLabel(wxString::Format("%u seconds", 
                                                                         timeleft_s));

          // Setup timer to update the timeout
          iUnansweredQuestionTimeoutUpdateTimer.Start(333);

        } else if (entry_timeout) {
          // Timed out! (Must handle whole window transition here, hence the other
          unansweredQuestiontimeoutLeftPanel->Show(false);
          ai->SetAnswer(KAnswerIgnore);
          unansweredQuestionPanel->Show(false);
          answeredQuestionPanel->Show(true);
          answeredQuestionStaticText->SetLabel("You chose not to answer to this question");
          
          // Stop timer
          iUnansweredQuestionTimeoutUpdateTimer.Stop();

        } else {
          // No timeout set
          unansweredQuestiontimeoutLeftPanel->Show(false);

          // Stop timer
          iUnansweredQuestionTimeoutUpdateTimer.Stop();

        }
      } else {
        // Handle panel visibility
        unansweredQuestionPanel->Show(false);
        answeredQuestionPanel->Show(true);
        
        // Now select current display behaviour
        switch(a) {
        case KAnswerYes:
          answeredQuestionStaticText->SetLabel("You answered 'Yes' to this question");
          break;
          
        case KAnswerNo:
          answeredQuestionStaticText->SetLabel("You answered 'No' to this question");
          break;
          
        case KAnswerIgnore:
          answeredQuestionStaticText->SetLabel("You chose not to answer to this question");
          break;
        }
      }
    } else {
      // This is not a Question
      iUnansweredQuestionTimeoutUpdateTimer.Stop();
      unansweredQuestionPanel->Show(false);
      answeredQuestionPanel->Show(false);
      
    }

    // Now, check whether Layout() needs to be redone
    if (box1 != answeredQuestionPanel->IsShown() ||
        box2 != unansweredQuestionPanel->IsShown()) {
      dPrint(ODDEBUG(1),"QuestionBox was removed or added, calling for Layout()");
      Layout();
    }
  }
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnListItemSelected( wxListEvent& event )
{
  // Lock
  CSync::Lock();

  SAlarmInfo *ai = (SAlarmInfo *)event.GetData();
  assert(ai != NULL);
  
  const MaCI::Alarm::TText *t = ai->alarm.GetText();
  const MaCI::Alarm::TAlarmEvent *ae = ai->alarm.GetAlarmEvent();
  const MaCI::Common::TTimestamp *ts = ai->alarm.GetTimestamp();

  if (t) {
    assert(t->text != NULL);
    assert(t->length > 0);
    const wxString str(t->text, t->length);
    
    eventDetailsTextCtrl->SetValue( str );
    
  } else {
    eventDetailsTextCtrl->SetValue( wxT("<no message>") );

  }

  // Call UpdateQuestionEntry function to take care of possible Question.
  UpdateQuestionEntry(ai);


  // Unlock
  CSync::Unlock();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnUnansweredQuestionTimeoutUpdateTimer( wxTimerEvent &event )
{
  assert(iUnansweredQuestionActiveEntry != NULL);

  CSync::Lock();
  UpdateQuestionEntry(iUnansweredQuestionActiveEntry);
  CSync::Unlock();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnAlarmMessage(wxCommandEvent &aEvent)
{
  RefreshEventList();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnUpdateStatusBar(wxCommandEvent &aEvent)
{
  
  // Now update the status bar text.
  wxString statustext;
  statustext.Printf("Connected to %u Alarm Services", iAlarmClientArray.size());
  dPrint(ODTEST,"Set statusBarText to '%s'", statustext.c_str());
  mainFrameStatusBar->SetStatusText(statustext, 0);
  
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnMenuSelection( wxCommandEvent& event )
{
  dPrint(ODTEST,"MenuSelection - Unhandled :) ");
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnAboutMenuSelect( wxCommandEvent& event )
{
  // Separate handler, as this involves creating a dialog.
  MaCI_AlarmAboutDialog aad(this, 
                            MaCI_AlarmVersion::GetVersionString(),
                            MaCI_AlarmVersion::GetBuildNumber());
  
  // Display form as Modal (this call will block)
  aad.ShowModal();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnEventsDisplayConfMenuItemSelection( wxCommandEvent& event )
{
  // Separate handler, as this involves creating a dialog.
  MaCI_AlarmEventsConfigDialog aecd(this);

  // Set to current state.
  aecd.SetDialogState(iEventsDisplayConfig);
  
  // Display form as Modal (this call will block)
  const int r = aecd.ShowModal();
  
  // Check for dialog return result.
  if (r == wxID_OK) {

    // Read state.
    aecd.GetDialogState(iEventsDisplayConfig);
  }

}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnQuitFileMenuItem( wxCommandEvent& event )
{
  // Forward to Close(). (Which will generate the
  // OnCloseApplicationEvent)
  Close();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnCloseApplicationEvent( wxCloseEvent& event )
{
  // Save current config. (state)
  SaveConfig();

  // Call destroy, effectively closing this component
  this->Destroy();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnClearEventsMenuItem( wxCommandEvent& event )
{
  CSync::Lock();
  iAlarmInfoArray.clear();
  eventListCtrl->DeleteAllItems();
  CSync::Unlock();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnYesButtonClick( wxCommandEvent& event )
{
  using namespace MaCI::Alarm;

  CSync::Lock();
  assert(iUnansweredQuestionActiveEntry != NULL);
  
  // Construct & send reply
  const CAlarmData &ad = iUnansweredQuestionActiveEntry->alarm;
  const TAlarmEvent *ae = ad.GetAlarmEvent();
  CAlarmClient *ac = iUnansweredQuestionActiveEntry->GetAlarmClientPtr();  
  CAlarmData rd;
  rd.CreateInternalBinBag();

  // The user replied 'Yes' to question.
  dPrint(ODTEST,"Sending QuestionReplyYes to '%s'", ac->GetDefaultTarget().c_str());
  rd.SetCommand(TCommand(KCommandUserQuestionReplyYes, ae->id));
  ac->SendCommand(rd);
  
  // Update internal status
  iUnansweredQuestionActiveEntry->SetAnswer(KAnswerYes);
  UpdateQuestionEntry(iUnansweredQuestionActiveEntry);
  CSync::Unlock();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnNoButtonClick( wxCommandEvent& event )
{
  using namespace MaCI::Alarm;

  CSync::Lock();
  assert(iUnansweredQuestionActiveEntry != NULL);
  
  // Construct & send reply
  const CAlarmData &ad = iUnansweredQuestionActiveEntry->alarm;
  const TAlarmEvent *ae = ad.GetAlarmEvent();
  CAlarmClient *ac = iUnansweredQuestionActiveEntry->GetAlarmClientPtr();  
  CAlarmData rd;
  rd.CreateInternalBinBag();
  // The user replied 'No' to question.
  dPrint(ODTEST,"Sending QuestionReplyNo to '%s'", ac->GetDefaultTarget().c_str());
  rd.SetCommand(TCommand(KCommandUserQuestionReplyNo, ae->id));
  ac->SendCommand(rd);
  
  // Update internal status
  iUnansweredQuestionActiveEntry->SetAnswer(KAnswerNo);
  UpdateQuestionEntry(iUnansweredQuestionActiveEntry);
  CSync::Unlock();
}
//*****************************************************************************

void MaCI_AlarmMainFrame::OnQuestionDialogEvent(wxCommandEvent &aEvent)
{
  using namespace MaCI::Alarm;
  SAlarmInfo *ai = static_cast<SAlarmInfo *>(aEvent.GetClientData());
  assert(ai != NULL);

  CSync::Lock();

  // Take easier pointer
  const CAlarmData &ad = ai->alarm;
  const TAlarmEvent *ae = ad.GetAlarmEvent();
  assert(ae != NULL);
  CAlarmClient *ac = ai->GetAlarmClientPtr();  
  assert(ac != NULL);

  CAlarmData rd;
  rd.CreateInternalBinBag();
  rd.SetTimestamp(MaCI::Common::TTimestamp());

  // Process.
  switch(ai->answer) {
  case KAnswerYes:
    rd.SetCommand(TCommand(KCommandUserQuestionReplyYes, ae->id));
    break;
  case KAnswerNo:
    rd.SetCommand(TCommand(KCommandUserQuestionReplyNo, ae->id));
    break;
  case KAnswerIgnore:
    rd.SetCommand(TCommand(KCommandUserQuestionReplyIgnore, ae->id));
    break;
  }

  // The user replied 
  dPrint(ODTEST,"Sending QuestionReply '%u' to '%s'", 
         ai->answer, ac->GetDefaultTarget().c_str());
  ac->SendCommand(rd);
  
  // Update internal status
  UpdateQuestionEntry(ai);

  // No longer protected.
  ai->SetProtected(false);

  CSync::Unlock();
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

bool MaCI_AlarmMainFrame::LoadConfig(void)
{
  bool result = false;
  result = iEventsDisplayConfig.LoadXMLFile(gConfigFileName, 
                                            gEventsDisplayConfigRootKey);

  return result;
}
//*****************************************************************************

bool MaCI_AlarmMainFrame::SaveConfig(void)
{
  bool result = false;
  result = iEventsDisplayConfig.SaveXMLFile(gConfigFileName, 
                                            gEventsDisplayConfigRootKey);

  return result;
}
//*****************************************************************************

void MaCI_AlarmMainFrame::RequestRefreshEventList(void)
{
  wxCommandEvent e( MY_EVENT_REFRESHEVENTLIST, GetId() );
  AddPendingEvent(e);
}
//*****************************************************************************

void MaCI_AlarmMainFrame::RequestUpdateStatusBar(void)
{
  wxCommandEvent e( MY_EVENT_UPDATESTATUSBAR, GetId() );
  AddPendingEvent(e);
}
//*****************************************************************************

void MaCI_AlarmMainFrame::DisconnectServices(void)
{
  dPrint(ODTEST,"DisconnectServices() (%u entries in array)", iAlarmClientArray.size());
  // Call Close() on all Clients and then delete.
  for(EACH_IN_i(iAlarmClientArray)) {
    (*i)->Close();
    delete *i;
  }
  
  // Clear the array.
  iAlarmClientArray.clear();
}
//*****************************************************************************

bool MaCI_AlarmMainFrame::ConnectServices(void) 
{
  bool result = iServiceList.size() > 0;
  dPrint(ODTEST,"ConnectServices()");

  assert(iGIMIPtr != NULL);

  for(EACH_IN_i(iServiceList)) {
    // Create new instance
    MaCI::Alarm::CAlarmClient *ac = new MaCI::Alarm::CAlarmClient(iGIMIPtr, i->minorType);
    assert(ac != NULL);

    dPrint(ODTEST,"Constructed new AlarmClient (Minor: %d)", i->minorType);
 
    // Assign
    dPrint(ODTEST,"Setting Default target : 0x%08x", i->targetID);
    ac->SetDefaultTarget(i->targetID);

    // Open!
    dPrint(ODTEST,"Calling Open()");
    MaCI::EMaCIError e = ac->Open();
    if (e == MaCI::KMaCIOK) {
      dPrint(ODTEST,"Successfully opened AlarmClient to target: '%u', using minor %u.", 
             i->targetID, i->minorType);

      // Register handler!
      ac->SetAlarmClientCallback(this);
      iAlarmClientArray.push_back(ac);

    } else {
      dPrint(ODTEST,"Failed to Connect to AlarmServer at target: '%u', using minor %u!",
             i->targetID, i->minorType);
    }
  }

  return result;
}
//*****************************************************************************

bool MaCI_AlarmMainFrame::ExecuteQuestionCheckBox(SAlarmInfo &ai)
{
  // Remember, this function is called without Mutex locked!
  
  using namespace MaCI::Alarm;
  bool result = false;
  const CAlarmData &ad = ai.alarm;
  const TAlarmEvent *ae = ad.GetAlarmEvent();
  const MaCI::Common::TTimestamp *ts = ad.GetTimestamp();
  const TText *body = ad.GetText();
  const TText *heading = ad.GetHeadingText();
  const TText *source = ad.GetSourceNameText();
  CAlarmClient *ac = ai.GetAlarmClientPtr();

  std::string headingMsg = std::string(heading?heading->text:"") + std::string(":\n\n");
  std::string bodyMsg = headingMsg + std::string(body?body->text:"");
  std::string sourceMsg;

  if (source) {
    // Source was set. so give it.
    sourceMsg = source->text;

  } else {
    // Source was not specified, so try to make up something.
    
    const MaCI::MaCICtrl::SMaCISL sl = ac->GetDefaultTargetMaCISL();
    if (sl.IsValidFQMaCISL()) {
      sourceMsg = sl.group + " (" + sl.ToMaCISLString() + ")" ;
    } else {
      sourceMsg = ac->GetDefaultTarget();
    }
  }

  // Separate handler, as this involves creating a dialog.
  MaCI_AlarmQuestionDialog *aqd = new MaCI_AlarmQuestionDialog(this);

  // Set to current state.
  aqd->SetQuestion(sourceMsg, bodyMsg, ae->timeout_s * 1000);

  // Store current CAlarmData pointer.
  aqd->SetAlarmInfoPtr(&ai);

  // Now; BEFORE executing the ShowModal() function, mark entry protected.
  ai.SetProtected(true);
  
  // Display form as Modal (this call will block)
  const int r = aqd->Show();

  // Push to array of active dialog boxes.
  iQuestionDialogBoxArray.push_back(aqd); // \todo Currently this list is never cleared :)

  return result;
}
//*****************************************************************************

void MaCI_AlarmMainFrame::RefreshEventList(void)
{
  using namespace MaCI::Alarm;

  CSync::Lock();

  // Add all items.
  for(typeof(iAlarmInfoArray.rbegin()) i = iAlarmInfoArray.rbegin();
      i != iAlarmInfoArray.rend();
      ++i) {
    
    if (i->listindex < 0) {

      gim::time event_time(true);
      
      // Try to get timestamp, if doesn't exist, get current time
      const CAlarmData &ad = i->alarm;
      const TAlarmEvent *ae = ad.GetAlarmEvent();
      const MaCI::Common::TTimestamp *ts = ad.GetTimestamp();
      const TText *body = ad.GetText();
      const TText *heading = ad.GetHeadingText();
      const TText *source = ad.GetSourceNameText();
      CAlarmClient *ac = i->GetAlarmClientPtr();

      // Check for enabled event type.
      if (! (iEventsDisplayConfig.enabledEvents & (1 << ae->type))) {
        dPrint(ODDEBUG(1), "Rejected displayed type '%s'", 
               MaCI::Alarm::GetAlarmEventTypeStr((MaCI::Alarm::EAlarmEventType)ae->type).c_str());
        continue;
      }

      // Handle time.
      if (ts) event_time = ts->GetGimTime();
      struct tm lt;
      const bool cr = event_time.getLocalTime(lt);
      assert(cr);
      wxString listheading;
      wxString listtype;
      wxString listsource;
      wxString listtime = wxString::Format("%02u:%02u:%02u", 
                                           lt.tm_hour, lt.tm_min, lt.tm_sec);
      
      // Check for source. append it if available.
      if (source) {
        listsource = wxString::Format("%s", source->text);
        
      } else {
        const MaCI::MaCICtrl::SMaCISL sl = ac->GetDefaultTargetMaCISL();
        if (sl.IsValidFQMaCISL()) {
          listsource = sl.ToMaCISLString();
        } else {
          listsource = ac->GetDefaultTarget();
        } 
      }
      
      // Check for heading, if not set. Display unknown.
      if (heading) {
        listheading = wxString::Format("%s", heading->text);
        
      } else if (body) {
        listheading = wxString(body->text, MIN(32, body->length));
        
      } else {
        listheading = "<Unknown>";

      }

      
      // Type.
      listtype = MaCI::Alarm::GetAlarmEventTypeStr((MaCI::Alarm::EAlarmEventType)ae->type);

      //////// Insert data in report grid.
      const int ientry = eventListCtrl->InsertItem(0, listtime);
      eventListCtrl->SetItem(ientry, 1, listtype);
      eventListCtrl->SetItem(ientry, 2, listsource);
      eventListCtrl->SetItem(ientry, 3, listheading);
      eventListCtrl->SetItemPtrData( ientry, (wxUIntPtr)&(*i) );
      
      // Store index to struct. (Marks it 'handled')
      i->listindex = ientry;
      

      // Check for enabled questions and check the type.
      if (iEventsDisplayConfig.isQuestionPopupEnabled && 
          ae->type == MaCI::Alarm::KEventTypeQuestion) {
        
        dPrint(ODTEST,"Got Question! and CheckBox is enabled!");
        CSync::Unlock();
        ExecuteQuestionCheckBox(*i);
        CSync::Lock();
      }

    } else {
      // First already added entry found. Terminate.
      break;

    }
  }
  
  // Check current number of elements and clear if necessary.
  if (iEventsDisplayConfig.isLimitEventsEnabled) {
    while (eventListCtrl->GetItemCount() > iEventsDisplayConfig.eventLimit) {
      eventListCtrl->DeleteItem(eventListCtrl->GetItemCount() - 1);
      
    }
  }

  CSync::Unlock();
  
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
